﻿/// Author:					Walter Ferrari
/// Created:				2010-01-18
/// ref:                    http://www.snello.it
/// The use and distribution terms for this software are covered by the 
/// Common Public License 1.0 (http://opensource.org/licenses/cpl.php)  
/// By using this software in any fashion, you are agreeing to be bound by 
/// the terms of this license.
///
/// You must not remove this notice, or any other, from this software.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Markup;
using System.Diagnostics;
using System.IO;
using XLSXparsingLib;
using System.Collections;
using System.Windows.Data;
using InteractChartCommonLib;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Reflection.Emit;
using System.Reflection;
using InteractChartLib.Controls;
using System.Windows.Controls.DataVisualization.Charting.Primitives;

namespace InteractChartLib
{
    [TemplatePart(Name = InteractChart.ElementMainPanelName, Type = typeof(StackPanel))]
    [TemplatePart(Name = InteractChart.ElementMainCanvasChartName, Type = typeof(Canvas))]
    [TemplatePart(Name = InteractChart.ElementMainChartName, Type = typeof(Chart))]
    [TemplatePart(Name = InteractChart.ElementSelectionName, Type = typeof(Rectangle))]
    [TemplatePart(Name = InteractChart.ElementGraphBuilderName, Type = typeof(StackPanel))]
    [TemplatePart(Name = InteractChart.ElementBuildGraphName, Type = typeof(Button))]
    [TemplatePart(Name = InteractChart.ElementMainChartDragDropName, Type = typeof(DataPointSeriesDragDropTarget))]
    [TemplatePart(Name = InteractChart.ElementZoomPlusCursorName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = InteractChart.ElementZoomMinusCursorName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = InteractChart.ElementZoomWindowCursorName, Type = typeof(FrameworkElement))]
    public class InteractChart : Control
    {

        internal IFileDropFormat _fileDropped;
        internal string _itemSelected;

        private List<ObservableCollection<Pair>> _dynItemsSource = new List<ObservableCollection<Pair>>();
        private int currentModality;

        internal StackPanel _elementMainPanel;
        internal const string ElementMainPanelName = "MainPanelElement";

        internal Canvas _elementMainCanvasChart;
        internal const string ElementMainCanvasChartName = "MainCanvasChartElement";

        internal Chart _elementMainChart;
        internal const string ElementMainChartName = "MainChartElement";

        internal Rectangle _elementSelection;
        internal const string ElementSelectionName = "SelectionElement";

        internal DataPointSeriesDragDropTarget _elementMainChartDragDrop;
        internal const string ElementMainChartDragDropName = "MainChartDragDropElement";

        internal FrameworkElement _elementZoomPlusCursor;
        internal const string ElementZoomPlusCursorName = "ZoomPlusCursorElement";

        internal FrameworkElement _elementZoomMinusCursor;
        internal const string ElementZoomMinusCursorName = "ZoomMinusCursorElement";

        internal FrameworkElement _elementZoomWindowCursor;
        internal const string ElementZoomWindowCursorName = "ZoomWindowCursorElement";


        #region [GRAPHBUILDER related]
        internal ComboBox _elementGraphType;
        internal const string ElementGraphTypeName = "GraphTypeElement";

        internal ComboBox _elementIndependentValue;
        internal const string ElementIndependentValueName = "IndependentValueElementName";

        internal ComboBox _elementDependentValue;
        internal const string ElementDependentValueName = "DependentValueElementName";

        internal Button _elementBuildGraph;
        internal const string ElementBuildGraphName = "BuildGraphElement";

        internal Expander _elementGraphBuilder;
        internal const string ElementGraphBuilderName = "GraphBuilderElement";
        #endregion

        private bool noSelection;
        private Point startPoint;
        private Point startPointOnCanvas;


        public event EventHandler TemplateApplied;

        public InteractChart()
        {
            this.DefaultStyleKey = typeof(InteractChart);

        }

        private void InitInteractiveParts()
        {
            # region [ MainPanel INITIALIZATION ]
            MainPanelElement.Width = this.Width;
            MainPanelElement.Height = this.Height;
            #endregion

            double horzRoomSpace = MainPanelElement.Margin.Left + MainPanelElement.Margin.Right;
            double vertRoomSpace = MainPanelElement.Margin.Top + MainPanelElement.Margin.Bottom;
            # region [ GraphBuilder INITIALIZATION ]
            //GraphBuilderElement.Width = (this.Width - horzRoomSpace) / 4;
            GraphBuilderElement.Height = this.Height - vertRoomSpace;
            GraphBuilderElement.IsExpanded = true;
            GraphTypeElement.Items.Add("LinesSerie");
            GraphTypeElement.SelectedIndex = 0;

            InitDepIndepParts();
            BuildGraphElement.Click += BuildGraphElement_Click;
            #endregion

            #region [Chart INITIALIZATION]
            MainChartElement.Width = (this.Width - horzRoomSpace) / 4 * 3;
            MainChartElement.Height = (this.Height - vertRoomSpace);// (this.Margin.Top + this.Margin.Bottom);

            MainChartElement.Visibility = Visibility.Visible;
            MainChartElement.AllowDrop = true;
            currentModality = (int)ChartContextMenuActions.NoZoom;
            #endregion



            #region [ Selection INITIALIZATION ]
            SelectionElement.Width = 0.0;
            SelectionElement.Height = 0.0;
            SelectionElement.Fill = ParseBrush("Yellow");
            SelectionElement.Stroke = ParseBrush("Red");
            SelectionElement.SetValue(Canvas.ZIndexProperty, 1);
            SelectionElement.Opacity = 0.3;
            noSelection = true;
            #endregion
        }

        private void InitDepIndepParts()
        {
            IEnumerable<IDictionary> datasource = FileDropped.GetData(ItemSelected);

            DependentValueElement.Items.Clear();
            IndependentValueElement.Items.Clear();

            if (datasource.Count() == 0)
                return;


            IDictionary firstRow = null;
            try
            {
                firstRow = datasource.First();
            }
            catch
            {
                return;
            }

            foreach (DictionaryEntry pair in firstRow)
            {

                DependentValueElement.Items.Add(new ListBoxItem { Content = pair.Value, DataContext = pair.Key });
                IndependentValueElement.Items.Add(new ListBoxItem { Content = pair.Value, DataContext = pair.Key });
            }


        }

        #region [ ELEMENTS ACCESS FUNCTIONS ]

        /// <summary>
        /// Gets or sets the main stackpanel element.
        /// </summary>
        /// <value>The main panel element.</value>
        private StackPanel MainPanelElement
        {
            get { return _elementMainPanel; }
            set { _elementMainPanel = value; }
        }


        /// <summary>
        /// Gets or sets the main canvas element.
        /// </summary>
        /// <value>The main chart element.</value>
        private Canvas MainCanvasChartElement
        {
            get { return _elementMainCanvasChart; }
            set { _elementMainCanvasChart = value; }
        }


        /// <summary>
        /// Gets or sets the main chart element.
        /// </summary>
        /// <value>The main chart element.</value>
        private Chart MainChartElement
        {
            get { return _elementMainChart; }
            set { _elementMainChart = value; }
        }


        /// <summary>
        /// Gets or sets the selection element.
        /// </summary>
        /// <value>The selection element.</value>
        private Rectangle SelectionElement
        {
            get { return _elementSelection; }
            set { _elementSelection = value; }
        }

        /// <summary>
        /// Gets or sets the main expander element.
        /// </summary>
        /// <value>The main chart element.</value>
        private Expander GraphBuilderElement
        {
            get { return _elementGraphBuilder; }
            set { _elementGraphBuilder = value; }
        }

        private ComboBox GraphTypeElement
        {
            get { return _elementGraphType; }
            set { _elementGraphType = value; }
        }

        private ComboBox IndependentValueElement
        {
            get { return _elementIndependentValue; }
            set { _elementIndependentValue = value; }
        }

        private ComboBox DependentValueElement
        {
            get { return _elementDependentValue; }
            set { _elementDependentValue = value; }
        }

        private Button BuildGraphElement
        {
            get { return _elementBuildGraph; }
            set { _elementBuildGraph = value; }
        }

        private IFileDropFormat FileDropped
        {
            get { return _fileDropped; }
            set { _fileDropped = value; }
        }

        private string ItemSelected
        {
            get { return _itemSelected; }
            set { _itemSelected = value; }
        }


        private DataPointSeriesDragDropTarget MainChartDragDropElement
        {
            get { return _elementMainChartDragDrop; }
            set { _elementMainChartDragDrop = value; }
        }

        private FrameworkElement ZoomPlusCursorElement
        {
            get { return _elementZoomPlusCursor; }
            set { _elementZoomPlusCursor = value; }
        }

        private FrameworkElement ZoomMinusCursorElement
        {
            get { return _elementZoomMinusCursor; }
            set { _elementZoomMinusCursor = value; }
        }

        private FrameworkElement ZoomWindowCursorElement
        {
            get { return _elementZoomWindowCursor; }
            set { _elementZoomWindowCursor = value; }
        }



        private FrameworkElement CurrentCursorElement
        {
            get
            {
                switch (currentModality)
                {
                    case (int)ChartContextMenuActions.NoZoom:
                        return null;
                    case (int)ChartContextMenuActions.ZoomPlus:
                        return ZoomPlusCursorElement;
                    case (int)ChartContextMenuActions.ZoomMinus:
                        return ZoomMinusCursorElement;
                    case (int)ChartContextMenuActions.ZoomWindow:
                        return ZoomWindowCursorElement;
                    default:
                        return null;
                }
            }
        }


        #endregion

        #region SelectionBkgBrush

        public Brush SelectionBkgBrush
        {
            get { return GetValue(SelectionBkgBrushProperty) as Brush; }
            set { SetValue(SelectionBkgBrushProperty, value); }
        }

        public static readonly DependencyProperty SelectionBkgBrushProperty =
            DependencyProperty.Register(
                "SelectionBkgBrush",
                typeof(Brush),
                typeof(InteractChart),
                null);

        #endregion


        #region SelectionBkgOpacity

        public double SelectionBkgOpacity
        {
            get { return (double)GetValue(SelectionBkgOpacityProperty); }
            set { SetValue(SelectionBkgOpacityProperty, value); }
        }

        public static readonly DependencyProperty SelectionBkgOpacityProperty =
            DependencyProperty.Register(
                "SelectionBkgOpacity",
                typeof(double),
                typeof(InteractChart),
                null);

        #endregion


        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            object mainPanelObj = GetTemplateChild(ElementMainPanelName);
            Debug.Assert(typeof(StackPanel).IsInstanceOfType(mainPanelObj) || (mainPanelObj == null),
                "The template part ElementMainPanelName is not an instance of StackPanel!");
            MainPanelElement = mainPanelObj as StackPanel;

            object selectionObj = GetTemplateChild(ElementSelectionName);
            Debug.Assert(typeof(Rectangle).IsInstanceOfType(selectionObj) || (selectionObj == null),
                "The template part ElementSelectionName is not an instance of Rectangle!");
            SelectionElement = selectionObj as Rectangle;

            object mainChartObj = GetTemplateChild(ElementMainChartName);
            Debug.Assert(typeof(Chart).IsInstanceOfType(mainChartObj) || (mainChartObj == null),
                "The template part ElementMainChartName is not an instance of Chart!");
            MainChartElement = mainChartObj as Chart;

            MainChartElement.Drop += MainChart_doDrop;
            MainChartElement.DragEnter += MainChart_doDragEnter;
            MainChartElement.DragLeave += MainChart_doDragLeave;


            object mainCanvasObj = GetTemplateChild(ElementMainCanvasChartName);
            Debug.Assert(typeof(Canvas).IsInstanceOfType(mainCanvasObj) || (mainCanvasObj == null),
                "The template part ElementMainCanvasName is not an instance of Canvas!");

            MainCanvasChartElement = mainCanvasObj as Canvas;

            MainCanvasChartElement.MouseLeftButtonDown += new MouseButtonEventHandler(MainCanvas_MouseLeftButtonDown);
            MainCanvasChartElement.MouseMove += new MouseEventHandler(MainCanvas_MouseMove);
            MainCanvasChartElement.MouseLeftButtonUp += new MouseButtonEventHandler(MainCanvas_MouseLeftButtonUp);
            MainCanvasChartElement.MouseRightButtonDown += new MouseButtonEventHandler(MainCanvas_MouseRightButtonDown);
            MainCanvasChartElement.MouseRightButtonUp += new MouseButtonEventHandler(MainCanvas_MouseRightButtonUp);


            object graphBuilderObj = GetTemplateChild(ElementGraphBuilderName);
            Debug.Assert(typeof(Expander).IsInstanceOfType(graphBuilderObj) || (graphBuilderObj == null),
                "The template part ElementGraphBuilderName is not an instance of Expander!");
            GraphBuilderElement = graphBuilderObj as Expander;

            object GraphTypeObj = GetTemplateChild(ElementGraphTypeName);
            Debug.Assert(typeof(ComboBox).IsInstanceOfType(GraphTypeObj) || (GraphTypeObj == null),
                "The template part GraphBuilderElementName is not an instance of ComboBox!");
            GraphTypeElement = GraphTypeObj as ComboBox;

            object IndependentValueObj = GetTemplateChild(ElementIndependentValueName);
            Debug.Assert(typeof(ComboBox).IsInstanceOfType(IndependentValueObj) || (IndependentValueObj == null),
                "The template part ElementIndependentValueName is not an instance of ComboBox!");
            IndependentValueElement = IndependentValueObj as ComboBox;

            object DependentValueObj = GetTemplateChild(ElementDependentValueName);
            Debug.Assert(typeof(ComboBox).IsInstanceOfType(DependentValueObj) || (DependentValueObj == null),
                "The template part ElementDependentValueName is not an instance of ComboBox!");
            DependentValueElement = DependentValueObj as ComboBox;

            object BuildGraphObj = GetTemplateChild(ElementBuildGraphName);
            Debug.Assert(typeof(Button).IsInstanceOfType(BuildGraphObj) || (BuildGraphObj == null),
                "The template part ElementDependentValueName is not an instance of Button!");
            BuildGraphElement = BuildGraphObj as Button;

            object MainChartDragDropObj = GetTemplateChild(ElementMainChartDragDropName);
            Debug.Assert(typeof(DataPointSeriesDragDropTarget).IsInstanceOfType(MainChartDragDropObj) || (MainChartDragDropObj == null),
                "The template part ElementMainChartDragDropName is not an instance of DataPointSeriesDragDropTarget!");
            MainChartDragDropElement = MainChartDragDropObj as DataPointSeriesDragDropTarget;

            MainChartDragDropElement.Drop += MainChartDragDrop_doDrop;

            object zoomPlusCursorObj = GetTemplateChild(ElementZoomPlusCursorName);
            Debug.Assert(typeof(FrameworkElement).IsInstanceOfType(zoomPlusCursorObj) || (zoomPlusCursorObj == null),
                "The template part ElementZoomPlusCursorName is not an instance of FrameworkElement!");
            ZoomPlusCursorElement = zoomPlusCursorObj as FrameworkElement;

            object zoomMinusCursorObj = GetTemplateChild(ElementZoomMinusCursorName);
            Debug.Assert(typeof(FrameworkElement).IsInstanceOfType(zoomMinusCursorObj) || (zoomMinusCursorObj == null),
                "The template part ElementZoomMinusCursorName is not an instance of FrameworkElement!");
            ZoomMinusCursorElement = zoomMinusCursorObj as FrameworkElement;

            object zoomWindowCursorObj = GetTemplateChild(ElementZoomWindowCursorName);
            Debug.Assert(typeof(FrameworkElement).IsInstanceOfType(zoomWindowCursorObj) || (zoomWindowCursorObj == null),
                "The template part ElementZoomWindowCursorName is not an instance of FrameworkElement!");
            ZoomWindowCursorElement = zoomWindowCursorObj as FrameworkElement;


            InitInteractiveParts();

            TemplateApplied(this, null);

        }


        #region [ MainGridElement HANDLERS ]
        void MainGrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            //ColorChangeContextMenu contextMenu = new ColorChangeContextMenu(ChangingRectangle);
            //contextMenu.Show(e.GetPosition(LayoutRoot));
        }

        void MainGrid_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            // handle the event so the default context menu is hidden
            e.Handled = true;
        }
        #endregion


        #region [ MainCanvasElement HANDLERS ]
        private void MainCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (MainChartElement.Series.Count == 0)
                return;

            LineSeries myLS = MainChartElement.Series[0] as LineSeries;

            switch (currentModality)
            {
                case (int)ChartContextMenuActions.NoZoom:
                    return;
                case (int)ChartContextMenuActions.ZoomPlus:
                    ScaleChart((Point)e.GetPosition(myLS), 2.0);
                    break;
                case (int)ChartContextMenuActions.ZoomMinus:
                    ScaleChart((Point)e.GetPosition(myLS), 0.5);
                    break;
                case (int)ChartContextMenuActions.ZoomWindow:
                    if (noSelection == true)
                    {
                        if (MainChartElement.Series.Count == 0)
                            return;
                        startPoint = (Point)e.GetPosition(myLS);
                        startPointOnCanvas =(Point)e.GetPosition(MainCanvasChartElement);
                        noSelection = false;
                    }
                    else
                    {
                        noSelection = true;
                    }
                    break;
                default:
                    return;
            }



        }

        private void MainCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            Point ptMouseOnCanvas = e.GetPosition(MainCanvasChartElement);

            MoveCursorToCurrentMousePos(ptMouseOnCanvas);

            if (noSelection == false)
            {
                if (MainChartElement.Series.Count == 0)
                    return;
                DrawSelection(startPointOnCanvas, ptMouseOnCanvas);
            }

        }

        private void MainCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {

            noSelection = true;

            if (MainChartElement.Series.Count == 0)
                return;

            if (currentModality == (int)ChartContextMenuActions.ZoomWindow)
            {
                ZoomWindowChart(e);
            }

        }
        #endregion

        #region [ MainChartElement HANDLERS ]

        private void MoveCursorToCurrentMousePos(Point mousePosPt)
        {
            if (CurrentCursorElement == null)
                return;

            CurrentCursorElement.Visibility = Visibility.Visible;
            // Set the coordinates of custom cursor accordingly with the mouse pointer coordinates
            CurrentCursorElement.SetValue(Canvas.LeftProperty, mousePosPt.X);
            CurrentCursorElement.SetValue(Canvas.TopProperty, mousePosPt.Y);
            CurrentCursorElement.SetValue(Canvas.ZIndexProperty, 100);

        }


        private void MainChartDragDrop_doDrop(object sender, Microsoft.Windows.DragEventArgs e)
        {
            if (e.Data == null)
                return;

            object drArgs = e.Data.GetData("System.Windows.Controls.ItemDragEventArgs");

            if (drArgs == null)
                return;

            ItemDragEventArgs dataDropped = drArgs as ItemDragEventArgs;

            if (dataDropped == null)
                return;

            SelectionCollection selectedData = dataDropped.Data as SelectionCollection;

            if (selectedData == null)
                return;

            BuildGraphFromDrop(selectedData);

        }

        private void MainCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;

        }

        private void MainCanvas_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            ChartContextMenu contextMenu = new ChartContextMenu();

            contextMenu.NotifyClosed += delegate(object ccsender, RoutedEventArgs cce)
            {
                this.OnChartContextMenuClosed(ccsender, cce);
            };

            contextMenu.Show(e.GetPosition(Application.Current.RootVisual));

            // old stuff with childWindow
            /*
            RightClickContextMenu chartContextMenu = new RightClickContextMenu();
            chartContextMenu.Closed += new EventHandler(OnChartContextMenuClosed);
            
            // positioning the child window
            Point ptClicked = e.GetPosition(Application.Current.RootVisual);
            chartContextMenu.VerticalAlignment = VerticalAlignment.Top;
            chartContextMenu.Margin = new Thickness(ptClicked.X, ptClicked.Y, 0, 0);

            chartContextMenu.Show();
            */

        }

        private void OnChartContextMenuClosed(object action, RoutedEventArgs e)
        {
            currentModality = (int)action;
            MouseButtonEventArgs em = e as MouseButtonEventArgs;
            HideAllCursors();
            switch (currentModality)
            {
                case (int)ChartContextMenuActions.NoZoom:
                    MainCanvasChartElement.Cursor = Cursors.Arrow;
                    // noSelection false
                    break;
                case (int)ChartContextMenuActions.ZoomPlus:
                case (int)ChartContextMenuActions.ZoomMinus:
                case (int)ChartContextMenuActions.ZoomWindow:
                    MainCanvasChartElement.Cursor = Cursors.None;
                    MoveCursorToCurrentMousePos(em.GetPosition(MainCanvasChartElement));
                    break;
                default:
                    MainCanvasChartElement.Cursor = Cursors.Arrow;
                    return;
            }


        }


        private void MainChart_doDrop(object sender, DragEventArgs e)
        {


        }

        private void MainChart_doDragEnter(object sender, DragEventArgs e)
        {
        }

        private void MainChart_doDragLeave(object sender, DragEventArgs e)
        {
            e.Handled = true;
        }
        #endregion

        #region [MainChartDragDropElement HANDLERS]
        private void MainChartDragDropElement_Drop(object sender, DragEventArgs e)
        {

        }
        #endregion


        private void BuildGraphFromDrop(SelectionCollection droppedData)
        {
            IEnumerator<Selection> ieSel = droppedData.GetEnumerator();

            // let's get the number of series (or columns) included in the data dropped
            ieSel.MoveNext();
            PropertyInfo[] propInfo = ieSel.Current.Item.GetType().GetProperties();
            int columnsNumber = propInfo.Count();

            for (int i = 0; i < columnsNumber - 1; i++)
                _dynItemsSource.Add(new ObservableCollection<Pair>());

            // reset any data linked to the chart
            foreach (ObservableCollection<Pair> pairCollection in _dynItemsSource)
                pairCollection.Clear();

            object _first = double.NaN;
            object _second = double.NaN;
            double firstMin, firstMax, secondMin, secondMax;
            firstMin = firstMax = secondMin = secondMax = double.NaN;

            // let's navigate through each item of the SelectionCollection (which represents a row in the datagrid)
            // and fill the observable collection created just before
            foreach (Selection sel in droppedData)
            {
                for (int i = 0; i < columnsNumber; i++)
                {
                    object cellvalue = sel.Item.GetType().GetProperties()[i].GetValue(sel.Item, null);
                    double dblValue;
                    if (ComparerHelper.TryConvertToDouble(cellvalue, out dblValue))
                    {
                        if (i == 0) // we assume the first column as the independent column
                        {
                            _first = dblValue;
                            firstMin = double.IsNaN(firstMin) ? dblValue : firstMin;
                            firstMin = firstMin > dblValue ? dblValue : firstMin;
                            firstMax = double.IsNaN(firstMax) ? dblValue : firstMax;
                            firstMax = firstMax < dblValue ? dblValue : firstMax;
                        }
                        else
                        {
                            _second = dblValue;

                            secondMin = double.IsNaN(secondMin) ? dblValue : secondMin;
                            secondMin = secondMin > dblValue ? dblValue : secondMin;
                            secondMax = double.IsNaN(secondMax) ? dblValue : secondMax;
                            secondMax = secondMax < dblValue ? dblValue : secondMax;

                            Pair newPairValue = new Pair { First = _first, Second = _second };
                            _dynItemsSource[i - 1].Add(newPairValue);
                        }
                    }
                    else
                    {
                        _first = double.NaN;
                    }

                }
            }

            // Add Axes
            MainChartElement.Axes.Clear();

            double rangeX = Math.Abs(firstMax - firstMin);
            double rangeY = Math.Abs(secondMax - secondMin);
            IAxis myAxisX = new LinearAxis
            {
                Orientation = AxisOrientation.X,
                Minimum = firstMin - rangeX / 10,
                Maximum = firstMax + rangeX / 10,
                ShowGridLines = true
            };
            MainChartElement.Axes.Add(myAxisX);

            IAxis myAxisY = new LinearAxis
            {
                Orientation = AxisOrientation.Y,
                Minimum = secondMin - rangeY / 10,
                Maximum = secondMax + rangeY / 10,
                ShowGridLines = true
            };
            MainChartElement.Axes.Add(myAxisY);

            MainChartElement.Series.Clear();
            
            int seriesNum = 0;
            foreach (ObservableCollection<Pair> newPairCollection in _dynItemsSource)
            {
                Random rnd = new Random();
                int genValue= rnd.Next(1,999);
                LineSeries mySerie = new LineSeries();
                mySerie.Name = "Serie" + seriesNum.ToString() + genValue.ToString();
                mySerie.Title = "";
                seriesNum += 1;

                mySerie.ItemsSource = newPairCollection;
                mySerie.IndependentValueBinding = new Binding("First");
                mySerie.DependentValueBinding = new Binding("Second");

                //
                MainChartElement.Series.Add(mySerie);

                MainChartElement.Title = FileDropped.GetFilename();
                MainChartElement.LegendTitle = "Legend";
            }

        }

        private void ShowChart()
        {
            IEnumerable<IDictionary> datasource = FileDropped.GetData(ItemSelected);

            ListBoxItem depItem = DependentValueElement.SelectedItem as ListBoxItem;
            ListBoxItem indepItem = IndependentValueElement.SelectedItem as ListBoxItem;

            if (depItem == null || indepItem == null)
                return;

            object dependentValueKey = depItem.DataContext;
            object independentValueKey = indepItem.DataContext;
            string graphType = GraphTypeElement.SelectedValue.ToString();

            foreach (ObservableCollection<Pair> pairCollection in _dynItemsSource)
                pairCollection.Clear();

            ObservableCollection<Pair> newPairCollection = new ObservableCollection<Pair>();
            object _first = double.NaN;
            object _second = double.NaN;
            double firstMin, firstMax, secondMin, secondMax;
            firstMin = firstMax = secondMin = secondMax = double.NaN;

            // assuming the first row contains headers
            foreach (IDictionary rowDict in datasource.Skip(1))
            {
                foreach (DictionaryEntry cell in rowDict)
                {
                    double dblValue;
                    if (cell.Key.Equals(independentValueKey))
                    {
                        if (ComparerHelper.TryConvertToDouble(cell.Value, out dblValue))
                        {
                            _first = dblValue;
                            firstMin = double.IsNaN(firstMin) ? dblValue : firstMin;
                            firstMin = firstMin > dblValue ? dblValue : firstMin;
                            firstMax = double.IsNaN(firstMax) ? dblValue : firstMax;
                            firstMax = firstMax < dblValue ? dblValue : firstMax;
                        }
                        else
                        {
                            _first = double.NaN;
                        }

                    }
                    if (cell.Key.Equals(dependentValueKey))
                    {
                        if (ComparerHelper.TryConvertToDouble(cell.Value, out dblValue))
                        {
                            _second = dblValue;

                            secondMin = double.IsNaN(secondMin) ? dblValue : secondMin;
                            secondMin = secondMin > dblValue ? dblValue : secondMin;
                            secondMax = double.IsNaN(secondMax) ? dblValue : secondMax;
                            secondMax = secondMax < dblValue ? dblValue : secondMax;
                        }
                        else
                        {
                            _second = double.NaN;
                        }
                    }

                }

                newPairCollection.Add(new Pair { First = _first, Second = _second });

            }

            _dynItemsSource.Add(newPairCollection);


            // Add Axes
            MainChartElement.Axes.Clear();

            double rangeX = Math.Abs(firstMax - firstMin);
            double rangeY = Math.Abs(secondMax - secondMin);
            IAxis myAxisX = new LinearAxis
            {
                Orientation = AxisOrientation.X,
                Minimum = firstMin - rangeX / 10,
                Maximum = firstMax + rangeX / 10,
                ShowGridLines = true
            };
            MainChartElement.Axes.Add(myAxisX);

            IAxis myAxisY = new LinearAxis
            {
                Orientation = AxisOrientation.Y,
                Minimum = secondMin - rangeY / 10,
                Maximum = secondMax + rangeY / 10,
                ShowGridLines = true
            };
            MainChartElement.Axes.Add(myAxisY);



            LineSeries mySerie = new LineSeries();
            mySerie.Name = dependentValueKey.ToString() + independentValueKey.ToString();
            mySerie.Title = depItem.Content;

            mySerie.ItemsSource = newPairCollection;
            mySerie.IndependentValueBinding = new Binding("First");
            mySerie.DependentValueBinding = new Binding("Second");

            MainChartElement.Series.Clear();
            MainChartElement.Series.Add(mySerie);

            MainChartElement.Title = FileDropped.GetFilename();
            MainChartElement.LegendTitle = "Legend";
            MainChartElement.DataContext = newPairCollection;
        }


        private FrameworkElement GetChartAreaElement(FrameworkElement child)
        {
            FrameworkElement parent = (FrameworkElement)VisualTreeHelper.GetParent(child);
            while (!(parent is EdgePanel) || parent.Name != "ChartArea")
            {
                if (parent is Border)
                    return null;
                parent = (FrameworkElement)VisualTreeHelper.GetParent(parent);
            }

            return parent;

        }

        private void DrawSelection(Point startPt, Point endPt)
        {
            //startPt = FromLineSeriesToCanvasCoord(startPt);
            //endPt = FromLineSeriesToCanvasCoord(endPt);
            
            if (startPt.X > endPt.X)
            {
                double tmp = startPt.X;
                startPt.X = endPt.X;
                endPt.X = tmp;
            }

            if (startPt.Y > endPt.Y)
            {
                double tmp = startPt.Y;
                startPt.Y = endPt.Y;
                endPt.Y = tmp;
            }
            
            SelectionElement.SetValue(Canvas.TopProperty, startPt.Y);
            SelectionElement.SetValue(Canvas.LeftProperty, startPt.X);
            SelectionElement.SetValue(Canvas.WidthProperty, endPt.X - startPt.X);
            SelectionElement.SetValue(Canvas.HeightProperty, endPt.Y - startPt.Y);
            SelectionElement.Visibility = Visibility.Visible;
        }


        private void ZoomWindowChart(MouseButtonEventArgs e)
        {
            LineSeries myLS = MainChartElement.Series[0] as LineSeries;
            Point endPoint = e.GetPosition(myLS);

            LinearAxis xAxis = MainChartElement.Axes[0] as LinearAxis;
            LinearAxis yAxis = MainChartElement.Axes[1] as LinearAxis;

            // values range of both axes
            double rangeX = Math.Abs(Convert.ToDouble(xAxis.ActualMaximum - xAxis.ActualMinimum));
            double rangeY = Math.Abs(Convert.ToDouble(yAxis.ActualMaximum - yAxis.ActualMinimum));

            // extent of the graph in pixel
            double pixelExtentX = Math.Abs((xAxis.GetPlotAreaCoordinate(Math.Abs((double)xAxis.ActualMaximum))).Value);
            double pixelExtentY = Math.Abs((yAxis.GetPlotAreaCoordinate(Math.Abs((double)yAxis.ActualMaximum))).Value);

            // calculate scale factor for both axes
            double xScalePixelFactor = rangeX / pixelExtentX;
            double yScalePixelFactor = rangeY / pixelExtentY;

            // calculate offset
            double xOffset = xAxis.GetPlotAreaCoordinate(0.0).Value;
            double yOffset = yAxis.GetPlotAreaCoordinate(0.0).Value;

            // new min & max values of the graph
            xAxis.Minimum = (Math.Min(startPoint.X, endPoint.X) - xOffset) * xScalePixelFactor;
            xAxis.Maximum = (Math.Max(startPoint.X, endPoint.X) - xOffset) * xScalePixelFactor;
            yAxis.Minimum = (pixelExtentY - Math.Max(startPoint.Y, endPoint.Y) - yOffset) * yScalePixelFactor;
            yAxis.Maximum = (pixelExtentY - Math.Min(startPoint.Y, endPoint.Y) - yOffset) * yScalePixelFactor;
            
            SelectionElement.Visibility = Visibility.Collapsed;

        }

        private void ScaleChart(Point ptClicked, double scaleFactor)
        {
            LineSeries myLS = MainChartElement.Series[0] as LineSeries;

            LinearAxis xAxis = MainChartElement.Axes[0] as LinearAxis;
            LinearAxis yAxis = MainChartElement.Axes[1] as LinearAxis;

            double newRangeX = Math.Abs(xAxis.Maximum.Value - xAxis.Minimum.Value) / scaleFactor;
            double newRangeY = Math.Abs(yAxis.Maximum.Value - yAxis.Minimum.Value) / scaleFactor;

            // scale factors between axes values and pixels
            double xScalePixelValuesFactor = (xAxis.Maximum.Value - xAxis.Minimum.Value) /
                                            (xAxis.GetPlotAreaCoordinate(xAxis.Maximum).Value - xAxis.GetPlotAreaCoordinate(xAxis.Minimum).Value);

            double yScalePixelValuesFactor = (yAxis.Maximum.Value - yAxis.Minimum.Value) /
                                            (yAxis.GetPlotAreaCoordinate(yAxis.Maximum).Value - yAxis.GetPlotAreaCoordinate(yAxis.Minimum).Value);
            // calculate offset
            double xOffset = xAxis.GetPlotAreaCoordinate(0.0).Value;
            double yOffset = yAxis.GetPlotAreaCoordinate(0.0).Value;
            double pixelExtentY = Math.Abs((yAxis.GetPlotAreaCoordinate(Math.Abs((double)yAxis.ActualMaximum))).Value);

            xAxis.Minimum = ((ptClicked.X -xOffset) * xScalePixelValuesFactor) - newRangeX / 2;
            xAxis.Maximum = ((ptClicked.X - xOffset) * xScalePixelValuesFactor) + newRangeX / 2;
            yAxis.Minimum = ((pixelExtentY- ptClicked.Y- yOffset) * yScalePixelValuesFactor) - newRangeY / 2;
            yAxis.Maximum = ((pixelExtentY-ptClicked.Y- yOffset) * yScalePixelValuesFactor) + newRangeY / 2;
      
        }

        private LineSeries GetLineSerie(string serieName)
        {
            foreach (LineSeries ls in MainChartElement.Series)
            {
                if (ls.Name == serieName)
                {
                    return ls;
                }
            }
            return null;
        }


        public void AttachDatasource(IFileDropFormat fileDropped, string itemSelected)
        {
            FileDropped = fileDropped;
            ItemSelected = itemSelected;
        }





        /// <summary>
        /// Returns a point in the chartCanvas coordinate system given a point in LineSeries coordinate system
        /// </summary>
        /// <param name="ptLS"></param>
        /// <returns></returns>
        private Point FromLineSeriesToCanvasCoord(Point ptLS)
        {
            if (MainChartElement.Series.Count == 0)
                throw new ArgumentException("There are no series in the chart!");

            // the MainchartCanvas (which includes the chart and other shapes used) has its
            // own coordinate system which is different from that one in LineSeries
            // In order to plot accurately the selected area we need to calculate the origin
            // of the coordinate system used by LineSeries accordingly with the coordinate system 
            // of the Canvas
            LineSeries myLS = MainChartElement.Series[0] as LineSeries;
            Point originInLS_Space = new Point(0.0, 0.0);
            GeneralTransform transformFromLSToCanvas = myLS.TransformToVisual(MainCanvasChartElement);
            Point originOfLSInCanvasSpace = transformFromLSToCanvas.Transform(originInLS_Space);

            double xValue = ptLS.X + originOfLSInCanvasSpace.X;
            double yValue = ptLS.Y + originOfLSInCanvasSpace.Y;

            return new Point { X = xValue, Y = yValue };
        }

        private void BuildGraphElement_Click(object sender, RoutedEventArgs e)
        {
            ShowChart();

        }



        /// <summary>
        /// Parses a brush from a string.
        /// </summary>
        /// <param name="value">The string (e.g. "Black", "#FF000000", etc.)</param>
        /// <returns>The brush.</returns>
        public static Brush ParseBrush(string value)
        {
            try
            {
                Canvas canvas = (Canvas)XamlReader.Load("<Canvas xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" Background=\"" + value + "\" />");
                return canvas.Background;
            }
            catch (XamlParseException)
            {
                return null;
            }
        }


        private void HideAllCursors()
        {
            ZoomWindowCursorElement.Visibility = Visibility.Collapsed;
            ZoomPlusCursorElement.Visibility = Visibility.Collapsed;
            ZoomMinusCursorElement.Visibility = Visibility.Collapsed;
        }
        // helper method for event handlers
        private static string GetSenderName(object sender)
        {
            FrameworkElement fe = sender as FrameworkElement;
            return fe.Name;
        }

    }





}
